#include "stdafx.h"
#include "CppUnitTest.h"
#include <HSModel/Human.h>
#include <HSModel/WorldInfoController.h>
#include <HSModel/SharedConstants.h>
#include <HSModel/World.h>
#include <HSModel/Ground.h>
#include <HSModel/Model3DStorage.h>


#include <memory>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace HSModelTests
  {
  TEST_CLASS(HumanTests)
    {
    public:

      class Mock_WorldInfoController : public IWorldInfoController
        {
        public:
          Mock_WorldInfoController() {};
          bool AvailableForWalking(const std::string& i_name, TVector2 i_point) const
            {
            return false;
            }
          double GetHeightForXY(TVector2 i_point) const
            {
            return 1.0;
            }
          void SetHumanPosition(const std::string& i_name, TVector3 i_position) {}
          bool CanHumanAcquireBalloon(const TVector3& i_position) {return false;};
        };

      class MWIC_1 : public Mock_WorldInfoController
        {
        public:
          MWIC_1() {};
          bool AvailableForWalking(const std::string& i_name, TVector2 i_point) const
            {
            if (55 < i_point.X() && 55 == i_point.Y())
              return true;
            return false;
            }
        };

      class MWIC_2 : public Mock_WorldInfoController
        {
        public:
          MWIC_2() {};
          bool AvailableForWalking(const std::string& i_name, TVector2 i_point) const
            {
            if (60 == i_point.X() && 60 < i_point.Y())
              return true;
            return false;
            }
        };

      class MWIC_3 : public Mock_WorldInfoController
        {
        public:
          MWIC_3() {};
          bool AvailableForWalking(const std::string& i_name, TVector2 i_point) const
            {
            if (70 == i_point.X() && 70  > i_point.Y())
              return true;
            return false;
            }
        };

      class MWIC_4 : public Mock_WorldInfoController
        {
        public:
          MWIC_4() {};
          bool AvailableForWalking(const std::string& i_name, TVector2 i_point) const
            {
            if (75 > i_point.X() && 75 == i_point.Y())
              return true;
            return false;
            }
        };

      TEST_METHOD(HumanShouldFaceRightDirection)
        {
        /* ---- Arrange ---- */
        World world;

        std::unique_ptr<MWIC_1> hwi1 (new MWIC_1());
        Human human1(world, HT_DEFAULT_HUMAN, TVector3(55, 55, 10), *hwi1);
        std::unique_ptr<MWIC_2> hwi2 (new MWIC_2());
        Human human2(world, HT_DEFAULT_HUMAN, TVector3(60, 60, 10), *hwi2);
        std::unique_ptr<MWIC_3> hwi3 (new MWIC_3());
        Human human3(world, HT_DEFAULT_HUMAN, TVector3(70, 70, 10), *hwi3);
        std::unique_ptr<MWIC_4> hwi4 (new MWIC_4());
        Human human4(world, HT_DEFAULT_HUMAN, TVector3(75, 75, 10), *hwi4);

        const double expected_angle1 = 90.0;
        const double expected_angle2 = 0.0;
        const double expected_angle3 = 180.0;
        const double expected_angle4 = 270.0;

        /* ---- Act ---- */
        human1.Move();
        human2.Move();
        human3.Move();
        human4.Move();
        const double actual_angle1 = human1.GetAngleAroundY();
        const double actual_angle2 = human2.GetAngleAroundY();
        const double actual_angle3 = human3.GetAngleAroundY();
        const double actual_angle4 = human4.GetAngleAroundY();

        /* ---- Assert ----*/
        Assert::AreEqual(expected_angle1, actual_angle1);
        Assert::AreEqual(expected_angle2, actual_angle2);
        Assert::AreEqual(expected_angle3, actual_angle3);
        Assert::AreEqual(expected_angle4, actual_angle4);
        }

      TEST_METHOD(HumanShouldAfraidOfOtherHumans)
        {
        /* ---- Arrange ---- */
        World world;
        world.GetGround()->SetGroundType(0, 0, GT_WALKWAY);
        world.GetGround()->SetGroundType(1, 0, GT_WALKWAY);
        world.GetGround()->SetGroundType(2, 0, GT_WALKWAY);
        world.GetGround()->SetGroundType(3, 0, GT_WALKWAY);
        world.GetGround()->SetGroundType(4, 0, GT_WALKWAY);

        std::map<std::string, IRenderable*> objects = world.Renderable();
        Ground ground = (*world.GetGround());
        WorldInfoController info(ground, objects);
        Human human1(world, HT_DEFAULT_HUMAN, TVector3(0, 1, 0), info);
        Human human2(world, HT_DEFAULT_HUMAN, TVector3(3, 1, 0), info);

        const double expected_difference = 3;
        /* ---- Act ---- */
        human1.Move();
        human2.Move();
        human1.Move();
        human2.Move();

        const TVector3 actual_position1 = human1.GetPosition();
        const TVector3 actual_position2 = human2.GetPosition();
        //rounding to higher
        const double difference = ceil(fabs(actual_position1.X() - actual_position2.X()));

        /* ---- Assert ----*/
        Assert::AreEqual(expected_difference, difference);

        }

			 TEST_METHOD(HumanShouldStandStill)
        {
        /* ---- Arrange ---- */
        World world;
        world.GetGround()->SetGroundType(0, 0, GT_WALKWAY);
        
        std::map<std::string, IRenderable*> objects = world.Renderable();
        Ground ground = (*world.GetGround());
        WorldInfoController info(ground, objects);
				TVector3 human_position(GroundConstants::CELL_SIZE / 2 + GroundConstants::ORIGIN_X, GroundConstants::CELL_SIZE / 2 + GroundConstants::ORIGIN_Y, 0);
				Human human(world, HT_DEFAULT_HUMAN, human_position, info);
        const double expected_difference = 3;
        /* ---- Act ---- */
        human.Move();
				bool change_spotted = false;
        const TVector3 actual_position = human.GetPosition();
				const double initial_direction = human.GetAngleAroundY();
				human.Move();
        const TVector3 actual_position1 = human.GetPosition();
				const double initial_direction1 = human.GetAngleAroundY();
				if (actual_position1 != actual_position && initial_direction1 != initial_direction)
					change_spotted = true;
				human.Move();
				const TVector3 actual_position2 = human.GetPosition();
				const double initial_direction2 = human.GetAngleAroundY();
				if (actual_position2 != actual_position && initial_direction2 != initial_direction)
					change_spotted = true;
        human.Move();
        const TVector3 actual_position3 = human.GetPosition();
				const double initial_direction3 = human.GetAngleAroundY();
				if (actual_position3 != actual_position && initial_direction3 != initial_direction)
					change_spotted = true;

        /* ---- Assert ----*/
        Assert::IsFalse(change_spotted);

        }
    };
  }